package cc.iotkit.plugins.modbus.service;

import cc.iotkit.common.utils.JsonUtils;
import cc.iotkit.plugin.core.IPluginConfig;
import cc.iotkit.plugin.core.IPluginScript;
import cc.iotkit.plugin.core.thing.IThingService;
import cc.iotkit.plugin.core.thing.actions.DeviceState;
import cc.iotkit.plugin.core.thing.actions.up.DeviceRegister;
import cc.iotkit.plugin.core.thing.actions.up.DeviceStateChange;
import cc.iotkit.plugin.core.thing.actions.up.PropertyReport;
import cc.iotkit.plugins.modbus.conf.ModbusConfig;
import cc.iotkit.script.IScriptEngine;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;

import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.msg.ModbusRequest;
import com.serotonin.modbus4j.msg.ModbusResponse;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest;
import com.serotonin.modbus4j.msg.WriteRegistersRequest;
import com.serotonin.modbus4j.sero.util.queue.ByteQueue;
import lombok.extern.slf4j.Slf4j;
import com.serotonin.modbus4j.ModbusFactory;

import com.fasterxml.jackson.core.type.TypeReference;
import com.gitee.starblues.bootstrap.annotation.AutowiredType;
import com.gitee.starblues.bootstrap.realize.PluginCloseListener;
import com.gitee.starblues.core.PluginCloseType;
import com.gitee.starblues.core.PluginInfo;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.ReferenceCountUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * @author sjg
 */
@Slf4j
@Service
public class ModbusRtuPlugin implements PluginCloseListener {

    @Autowired
    private PluginInfo pluginInfo;

    @Autowired
    @AutowiredType(AutowiredType.Type.MAIN_PLUGIN)
    private IPluginScript pluginScript;

    @Autowired
    @AutowiredType(AutowiredType.Type.MAIN_PLUGIN)
    private IThingService thingService;

    private IScriptEngine scriptEngine;

    @Autowired
    private ModbusConfig modbusConfig;

    @Autowired
    @AutowiredType(AutowiredType.Type.MAIN_PLUGIN)
    private IPluginConfig pluginConfig;

    // 产品Key
    private String productKey;

    private List<String> deviceNames = new ArrayList<>();

//    private ModbusTcpMasterConfig config;
//    private ModbusTcpMaster master;

    private final Set<Integer> registeredDevice = new HashSet<>();

    private int[] slaves;

    private final Map<Integer, String> DATA_CACHE = new HashMap<>();

    static ModbusFactory modbusFactory;
    //建立链接
    static public ModbusMaster tcpMaster;


    @PostConstruct
    public void init() {
        // 新生成一个插件配置MAP
        Map<String, Object> modBusconfig = pluginConfig.getConfig(pluginInfo.getPluginId());
        log.info("get config:{}", JsonUtils.toJsonString(modBusconfig));
        // 拷贝modbusConfig进刚创建的MAP中
        BeanUtil.copyProperties(modBusconfig, modbusConfig, CopyOptions.create().ignoreNullValue());

        if(0 < modBusconfig.size()){
            // 设置modbus-tcp的ip和port
            if(modbusFactory == null) {
                modbusFactory = new ModbusFactory();
                tcpMaster = getMaster(modbusConfig.getHost(), modbusConfig.getPort());
                productKey = modbusConfig.getProductKey();
                deviceNames = Arrays.asList(modbusConfig.getDeviceName().split(","));
            } else {
                log.info("Rtu factory is null");
            }

        } else {
            // 产品Key
            productKey= new String("mrpi2yDSn7z2Kznp");
            deviceNames.add(new String("23032890"));

            // 设置modbus-tcp的ip和port
            tcpMaster = getMaster(modbusConfig.getHost(), modbusConfig.getPort());
            log.info("modbus plugin config info is null");
        }

        //获取脚本引擎
        scriptEngine = pluginScript.getScriptEngine(pluginInfo.getPluginId());
    }

    /**
     * 获取master
     *
     * @return
     * @throw ModbusInitException
     */
    public static ModbusMaster getMaster(String ip, Integer port) {
        IpParameters params = new IpParameters();
        params.setHost(ip);
        params.setPort(port);

        // RTU
        // createRtuMaster(com.serotonin.modbus4j.serial.SerialPortWrapper wrapper)
        params.setEncapsulated(true);
        ModbusMaster master = modbusFactory.createTcpMaster(params, true);
        //TCP
        // ModbusMaster master = modbusFactory.createTcpMaster(params, false);

        try {
            master.init();
        } catch (ModbusInitException e) {
            e.printStackTrace();
        }

        return master;
    }

    /**
     * 读取保持寄存器 功能码[03]
     *
     * @param start      开始地址
     * @param readLength 读取数量
     * @return
     * @throws ModbusInitException
     */
    public ByteQueue modbusTCP03(int slaveId, ModbusMaster tcpMaster, int start, int readLength) {
        ModbusRequest modbusRequest = null;
        try {
            modbusRequest = new ReadHoldingRegistersRequest(slaveId, start, readLength);
        } catch (ModbusTransportException e) {
            e.printStackTrace();
        }

        ModbusResponse modbusResponse = null;
        try {
            modbusResponse = tcpMaster.send(modbusRequest);
        } catch (ModbusTransportException e) {
            e.printStackTrace();
        }

        ByteQueue byteQueue = new ByteQueue(12);
        modbusRequest.write(byteQueue);
        System.out.println("功能码:" + modbusRequest.getFunctionCode());
        System.out.println("从站地址:" + modbusRequest.getSlaveId());
        System.out.println("开始地址:" + start);
        System.out.println("收到的响应信息大小:" + byteQueue.size());
        System.out.println("收到的响应信息值:" + byteQueue);
        return byteQueue;
    }


    /**
     * 写多个寄存器 功能码[16]
     * WriteCoilRequest  05
     * WriteRegisterRequest 06
     * WriteRegistersRequest 16
     *
     */
    public ByteQueue modbusTCP16(int slaveId, ModbusMaster tcpMaster, int writeOffset, short[] data) throws Exception {
        WriteRegistersRequest writeRegistersRequest = null;
        //收到响应
        ModbusResponse modbusResponse = null;
        try {
            writeRegistersRequest = new WriteRegistersRequest(slaveId, writeOffset, data);
            modbusResponse = tcpMaster.send(writeRegistersRequest);
            if (modbusResponse.isException()) {
                System.out.println("Exception response: message=" + modbusResponse.getExceptionMessage());
            } else {
                System.out.println("Success");
            }
        } catch (ModbusTransportException e) {
            e.printStackTrace();
        }
        ByteQueue byteQueue = new ByteQueue(12);
        modbusResponse.write(byteQueue);
        System.out.println("功能码:" + writeRegistersRequest.getFunctionCode());
        System.out.println("从站地址:" + writeRegistersRequest.getSlaveId());
        System.out.println("收到的响应信息大小:" + byteQueue.size());
        System.out.println("收到的响应信息值:" + byteQueue);
        return byteQueue;
    }

    @Scheduled(initialDelay = 3000, fixedDelay = 1000)
    public void taskRead() {
        for (int slave : slaves) {
            ByteQueue response = modbusTCP03(1, tcpMaster, 0, 255);


                String rspBytes = ByteBufUtil.hexDump(response.peekAll());
                ReferenceCountUtil.release(response);

                log.info("receive Response: " + rspBytes);
                //相同数据不处理
                if (DATA_CACHE.getOrDefault(slave+1, "").equals(rspBytes)) {
                    return;
                }
                DATA_CACHE.put(slave+1, rspBytes);

                if (!registeredDevice.contains(slave+1)) {
                    //第一次读取自动注册设备
                    thingService.post(pluginInfo.getPluginId(), DeviceRegister.builder()
                            .id(UUID.randomUUID().toString())
                            .productKey(productKey)
                            .deviceName(deviceNames.get(slave))
                            .build());
                    registeredDevice.add(slave+1);
                    //并上线
                    thingService.post(pluginInfo.getPluginId(), DeviceStateChange.builder()
                            .id(UUID.randomUUID().toString())
                            .productKey(productKey)
                            .deviceName(deviceNames.get(slave))
                            .state(DeviceState.ONLINE)
                            .build());
                }

                //调用脚本解码
                Map<String, Object> rst = scriptEngine.invokeMethod(new TypeReference<>() {
                }, "decode", rspBytes);
                if (rst == null || rst.isEmpty()) {
                    log.info("null");
                    return;
                }

                //属性上报
                thingService.post(pluginInfo.getPluginId(), PropertyReport.builder()
                        .id(UUID.randomUUID().toString())
                        .productKey(productKey)
                        .deviceName(deviceNames.get(slave))
                        .params(rst)
                        .build());

        }
    }

    @Override
    public void close(GenericApplicationContext applicationContext, PluginInfo pluginInfo, PluginCloseType closeType) {
        try {
            if (tcpMaster.isConnected()){
                tcpMaster.destroy();
            };
            if (closeType == PluginCloseType.UNINSTALL) {
                log.info("modbus plugin UNINSTALL：{}", pluginInfo.getPluginId());
            } else if (closeType == PluginCloseType.STOP) {
                log.info("modbus plugin STOP：{}", pluginInfo.getPluginId());
            } else if (closeType == PluginCloseType.UPGRADE_UNINSTALL) {
                log.info("modbus plugin UPGRADE_UNINSTALL：{}", pluginInfo.getPluginId());
            }
        } catch (Throwable e) {
            log.error("modbus plugin stop error", e);
        }
    }

}
